/*****************************************************************************
 *   Copyright(C)2009-2022 by VSF Team                                       *
 *                                                                           *
 *  Licensed under the Apache License, Version 2.0 (the "License");          *
 *  you may not use this file except in compliance with the License.         *
 *  You may obtain a copy of the License at                                  *
 *                                                                           *
 *     http://www.apache.org/licenses/LICENSE-2.0                            *
 *                                                                           *
 *  Unless required by applicable law or agreed to in writing, software      *
 *  distributed under the License is distributed on an "AS IS" BASIS,        *
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. *
 *  See the License for the specific language governing permissions and      *
 *  limitations under the License.                                           *
 *                                                                           *
 ****************************************************************************/

/*============================ INCLUDES ======================================*/

#include "hal/vsf_hal_cfg.h"

#if VSF_HAL_USE_FLASH == ENABLED

#include "hal/vsf_hal.h"

// HW
// for vendor headers
#include "hal/driver/vendor_driver.h"
// HW end

/*============================ MACROS ========================================*/

/*\note VSF_HW_FLASH_CFG_MULTI_CLASS is only for drivers for specified device(hw drivers).
 *      For other drivers, please define VSF_${FLASH_IP}_FLASH_CFG_MULTI_CLASS in header file.
 */

// HW
#ifndef VSF_HW_FLASH_CFG_MULTI_CLASS
#   define VSF_HW_FLASH_CFG_MULTI_CLASS         VSF_FLASH_CFG_MULTI_CLASS
#endif
// HW end

// HW
#define VSF_FLASH_CFG_IMP_PREFIX                vsf_hw
#define VSF_FLASH_CFG_IMP_UPCASE_PREFIX         VSF_HW
// HW end

#define ROM_FLASH_WRITE(__START_ADDR, __BUF, __LEN)                             \
                                                ((uint32_t (*)(uint32_t, uint8_t*, uint32_t))0x1fff7b81)((__START_ADDR), (__BUF), (__LEN))
#define ROM_FLASH_ERASE_ONE_SECTOR(__ADDR)      ((uint32_t (*)(uint32_t))0x1fff7c81)(__ADDR)

/*============================ MACROFIED FUNCTIONS ===========================*/
/*============================ TYPES =========================================*/

// HW
typedef struct VSF_MCONNECT(VSF_FLASH_CFG_IMP_PREFIX, _flash_t) {
#if VSF_HW_FLASH_CFG_MULTI_CLASS == ENABLED
    vsf_flash_t vsf_flash;
#endif
    // TODO: fix reg to the real type
    void *reg;
    vsf_flash_isr_t isr;
    uint32_t irq_mask;
} VSF_MCONNECT(VSF_FLASH_CFG_IMP_PREFIX, _flash_t);
// HW end

/*============================ GLOBAL VARIABLES ==============================*/
/*============================ LOCAL VARIABLES ===============================*/
/*============================ PROTOTYPES ====================================*/
/*============================ IMPLEMENTATION ================================*/

/*\note Implementation below is for hw flash only, because there is no requirements
 *          on the APIs of IPCore drivers.
 *      Usage of VSF_MCONNECT is not a requirement, but for convenience only,
 */

// HW
vsf_err_t VSF_MCONNECT(VSF_FLASH_CFG_IMP_PREFIX, _flash_init)(
    VSF_MCONNECT(VSF_FLASH_CFG_IMP_PREFIX, _flash_t) *flash_ptr,
    vsf_flash_cfg_t *cfg_ptr
) {
    VSF_HAL_ASSERT(flash_ptr != NULL);
    flash_ptr->irq_mask = 0;
    flash_ptr->isr = cfg_ptr->isr;
    return VSF_ERR_NONE;
}

void VSF_MCONNECT(VSF_FLASH_CFG_IMP_PREFIX, _flash_fini)(
    VSF_MCONNECT(VSF_FLASH_CFG_IMP_PREFIX, _flash_t) *flash_ptr
) {
    VSF_HAL_ASSERT(flash_ptr != NULL);
}

fsm_rt_t VSF_MCONNECT(VSF_FLASH_CFG_IMP_PREFIX, _flash_enable)(
    VSF_MCONNECT(VSF_FLASH_CFG_IMP_PREFIX, _flash_t) *flash_ptr
) {
    VSF_HAL_ASSERT(flash_ptr != NULL);
    return fsm_rt_cpl;
}

fsm_rt_t VSF_MCONNECT(VSF_FLASH_CFG_IMP_PREFIX, _flash_disable)(
    VSF_MCONNECT(VSF_FLASH_CFG_IMP_PREFIX, _flash_t) *flash_ptr
) {
    VSF_HAL_ASSERT(flash_ptr != NULL);
    return fsm_rt_cpl;
}

void VSF_MCONNECT(VSF_FLASH_CFG_IMP_PREFIX, _flash_irq_enable)(
    VSF_MCONNECT(VSF_FLASH_CFG_IMP_PREFIX, _flash_t) *flash_ptr,
    vsf_flash_irq_mask_t irq_mask
) {
    VSF_HAL_ASSERT(flash_ptr != NULL);
    flash_ptr->irq_mask |= irq_mask;
}

void VSF_MCONNECT(VSF_FLASH_CFG_IMP_PREFIX, _flash_irq_disable)(
    VSF_MCONNECT(VSF_FLASH_CFG_IMP_PREFIX, _flash_t) *flash_ptr,
    vsf_flash_irq_mask_t irq_mask
) {
    VSF_HAL_ASSERT(flash_ptr != NULL);
    irq_mask &= ~irq_mask;
}

vsf_err_t VSF_MCONNECT(VSF_FLASH_CFG_IMP_PREFIX, _flash_erase_multi_sector)(
    VSF_MCONNECT(VSF_FLASH_CFG_IMP_PREFIX, _flash_t) *flash_ptr,
    vsf_flash_size_t offset,
    vsf_flash_size_t size
) {
    VSF_HAL_ASSERT(flash_ptr != NULL);
    VSF_HAL_ASSERT(!(offset & (4096 - 1)));

    offset += FLASH_BASE;
    for (vsf_flash_size_t i = 0; i < size; i += 4096) {
        if (0 != ROM_FLASH_ERASE_ONE_SECTOR(offset + i)) {
            return VSF_ERR_FAIL;
        }
    }

    vsf_flash_isr_t *isr_ptr = &flash_ptr->isr;
    if (    (flash_ptr->irq_mask & VSF_FLASH_IRQ_ERASE_MASK)
        &&  (isr_ptr->handler_fn != NULL)) {
        isr_ptr->handler_fn(isr_ptr->target_ptr, (vsf_flash_t *)flash_ptr, VSF_FLASH_IRQ_ERASE_MASK);
    }
    return VSF_ERR_NONE;
}

vsf_err_t VSF_MCONNECT(VSF_FLASH_CFG_IMP_PREFIX, _flash_write_multi_sector)(
    VSF_MCONNECT(VSF_FLASH_CFG_IMP_PREFIX, _flash_t) *flash_ptr,
    vsf_flash_size_t offset,
    uint8_t *buffer,
    vsf_flash_size_t size
) {
    VSF_HAL_ASSERT(flash_ptr != NULL);
    VSF_HAL_ASSERT(NULL != buffer);

    vsf_err_t result = (0 == ROM_FLASH_WRITE(offset + FLASH_BASE, buffer, size)) ? VSF_ERR_NONE : VSF_ERR_FAIL;

    vsf_flash_isr_t *isr_ptr = &flash_ptr->isr;
    if (    (flash_ptr->irq_mask & VSF_FLASH_IRQ_WRITE_MASK)
        &&  (isr_ptr->handler_fn != NULL)) {
        isr_ptr->handler_fn(isr_ptr->target_ptr, (vsf_flash_t *)flash_ptr, VSF_FLASH_IRQ_WRITE_MASK);
    }
    return result;
}

vsf_err_t VSF_MCONNECT(VSF_FLASH_CFG_IMP_PREFIX, _flash_read_multi_sector)(
    VSF_MCONNECT(VSF_FLASH_CFG_IMP_PREFIX, _flash_t) *flash_ptr,
    vsf_flash_size_t offset,
    uint8_t *buffer,
    vsf_flash_size_t size
) {
    VSF_HAL_ASSERT(flash_ptr != NULL);
    VSF_HAL_ASSERT(NULL != buffer);

    memcpy(buffer, (void *)(offset + FLASH_BASE), size);

    vsf_flash_isr_t *isr_ptr = &flash_ptr->isr;
    if (    (flash_ptr->irq_mask & VSF_FLASH_IRQ_READ_MASK)
        &&  (isr_ptr->handler_fn != NULL)) {
        isr_ptr->handler_fn(isr_ptr->target_ptr, (vsf_flash_t *)flash_ptr, VSF_FLASH_IRQ_READ_MASK);
    }
    return VSF_ERR_NONE;
}

vsf_flash_status_t VSF_MCONNECT(VSF_FLASH_CFG_IMP_PREFIX, _flash_status)(
    VSF_MCONNECT(VSF_FLASH_CFG_IMP_PREFIX, _flash_t) *flash_ptr
) {
    return (vsf_flash_status_t) {
        .is_busy          = 0,
    };
}

/*\note Implementation of APIs below is optional, because there is default implementation in flash_template.inc.
 *      VSF_FLASH_CFG_REIMPLEMENT_API_XXXX can be defined to ENABLED to re-write the default implementation for better performance.
 *
 *      The list of APIs and configuration:
 *      VSF_FLASH_CFG_REIMPLEMENT_API_GET_CONFIGURATION for flash_get_configuration.
 *          Default implementation will assert(false) to indicate the feature is not implemented.
 *      VSF_FLASH_CFG_REIMPLEMENT_API_CAPABILITY for flash_capability.
 *          Default implementation will use macros below to initialize capability structure:
 *              VSF_FLASH_CFG_CAPABILITY_IRQ_MASK (default: VSF_FLASH_IRQ_ALL_BITS_MASK)
 *              VSF_FLASH_CFG_CAPABILITY_BASE_ADDRESS (default: 0)
 *              VSF_FLASH_CFG_CAPABILITY_MAX_SIZE (default: error if not defined)
 *              VSF_FLASH_CFG_CAPABILITY_ERASE_SECTORE_SIZE (default: 4096)
 *              VSF_FLASH_CFG_CAPABILITY_WRITE_SECTORE_SIZE (default: 512)
 *              VSF_FLASH_CFG_CAPABILITY_NONE_SECTOR_ALIGNED_WRITE (default: 0)
 *              VSF_FLASH_CFG_CAPABILITY_NONE_SECTOR_ALIGNED_READ (default: 1)
 */

vsf_flash_capability_t VSF_MCONNECT(VSF_FLASH_CFG_IMP_PREFIX, _flash_capability)(
    VSF_MCONNECT(VSF_FLASH_CFG_IMP_PREFIX, _flash_t) *flash_ptr
) {
    uint32_t flash_size = (DBG->ID >> 6) & 3;
    flash_size =    (flash_size == 1) ? 2048
                :   (flash_size == 2) ? 4096
                :   0;
    VSF_HAL_ASSERT(flash_size > 0);

    return (vsf_flash_capability_t) {
        .irq_mask              = VSF_FLASH_IRQ_ALL_BITS_MASK,
        .base_address          = FLASH_BASE,
        .max_size              = (flash_size - 128) * 1024,
        .erase_sector_size     = 4096,
        .write_sector_size     = 256,
        .none_sector_aligned_write = 1,
        .none_sector_aligned_read  = 1,
    };
}
// HW end

/*============================ INCLUDES ======================================*/

/*\note TODO: add comments about template configurations below:
 *  VSF_FLASH_CFG_ERASE_ALL_TEMPLATE                implement erase_all by erase_multi_sector in template
 *  VSF_FLASH_CFG_ERASE_ONE_SECTOR_TEMPLATE         implement erase_one_sector by erase_multi_sector in template
 *  VSF_FLASH_CFG_WRITE_ONE_SECTOR_TEMPLATE         implement write_one_sector by write_multi_sector in template
 *  VSF_FLASH_CFG_READ_ONE_SECTOR_TEMPLATE          implement read_one_sector by read_multi_sector in template
 */

// only define in source file
#define VSF_FLASH_CFG_REIMPLEMENT_API_CAPABILITY        ENABLED
#define VSF_FLASH_CFG_ERASE_ALL_TEMPLATE                ENABLED
#define VSF_FLASH_CFG_ERASE_ONE_SECTOR_TEMPLATE         ENABLED
#define VSF_FLASH_CFG_WRITE_ONE_SECTOR_TEMPLATE         ENABLED
#define VSF_FLASH_CFG_READ_ONE_SECTOR_TEMPLATE          ENABLED

#define VSF_FLASH_CFG_IMP_LV0(__IDX, __HAL_OP)                                  \
    VSF_MCONNECT(VSF_FLASH_CFG_IMP_PREFIX, _flash_t)                            \
        VSF_MCONNECT(VSF_FLASH_CFG_IMP_PREFIX, _flash ## __IDX) = {             \
        __HAL_OP                                                                \
    };
#include "hal/driver/common/flash/flash_template.inc"

#endif      // VSF_HAL_USE_FLASH
